/*
 * REFSocket.cpp
 *
 *  Created on: Apr 14, 2013
 *      Author: Lucifer
 */
#include "../pch.h"

namespace BFX
{

/**
 * Initializes a new instance of the socket class using the specified address family, socket type and protocol.
 * @param[in] addressFamily One of the BFX::SocketAddressFamily values.
 * @param[in] socketType One of the BFX::SocketType values.
 * @param[in] protocolType One of the BFX::SocketProtocolType values.
  */
REFSocket::REFSocket(SocketAddressFamily addressFamily, SocketType socketType, SocketProtocolType protocolType)
{
	StaticInitialize();

	m_hSocket = INVALID_SOCKET;
	m_lastError = SocketError_Success;

	m_bIsConnected = false;
	m_bIsBlocking = true;
	m_bIsRealBlocking = true;
	m_bIsListening = false;

	SOCKET hSocket = socket(addressFamily, socketType, protocolType);

	BFX_ASSERT(hSocket != INVALID_SOCKET);
	if (hSocket == INVALID_SOCKET)
	{
		// rises an exception with last system error code of current thread.
		throw SocketException();
	}
	m_hSocket		= hSocket;
	m_addressFamily	= addressFamily;
	m_socketType	= socketType;
	m_protocolType	= protocolType;
}

// Called by creates a socket to accept an incoming request.
REFSocket::REFSocket(SOCKET hSocket)
{
	BFX_ASSERT(hSocket != INVALID_SOCKET);

	m_lastError = SocketError_Success;

	m_bIsConnected = false;
	m_bIsBlocking = true;
	m_bIsRealBlocking = true;
	m_bIsListening = false;

	m_hSocket = hSocket;
	m_addressFamily	= SocketAddressFamily_Unknown;
	m_socketType	= SocketType_Unknown;
	m_protocolType	= SocketProtocolType_Unknown;
}

/**
 * Destroys the instance of the socket class.
 */
REFSocket::~REFSocket()
{
	Close(0);
}

void REFSocket::StaticInitialize()
{
#ifdef	_WIN32
	// Windows system needs to initializes network environment on first time use socket.
	static bool	s_bInitialized = false;
	static Mutex	s_syncLock;

	// WIN32 first time initialization
	if (!s_bInitialized)
	{
		MutexHolder hold(&s_syncLock);
		if (!s_bInitialized)
		{
			WSADATA wsaData;
			int nRetVal = WSAStartup(MAKEWORD(2, 2), &wsaData);
			BFX_ENSURE1(nRetVal == 0, "WSAStartup failed.");
			s_bInitialized = true;
		}
	}
#endif	//	_WIN32
}

//
// Attributes
//

/**
 * Returns file descriptor.
 * @note: any external operations on it are dangerous.
 * @return The file descriptor, or INVALID_SOCKET if not yet open or already closed.
 */
SOCKET REFSocket::GetFileDescriptor() const
{
	return m_hSocket;
}

/**
 * Gets the address family of the socket.
 * @return One of the BFX::SocketAddressFamily values.
 */
SocketAddressFamily REFSocket::GetAddressFamily() const
{
	return m_addressFamily;
}

/**
 * Gets the protocol type of the socket.
 * @return One of the BFX::SocketProtocolType values.
 */
SocketProtocolType REFSocket::GetProtocolType() const
{
	return m_protocolType;
}

/**
 * Gets the type of the Socket.
 * @return One of the BFX::SocketType values.
 */
SocketType REFSocket::GetSocketType() const
{
	return m_socketType;
}

/**
 * Get the error status for the last socket operation that failed.
 * @return The return value indicates the error code for the last operation of this socket.
 */
SocketError REFSocket::GetLastError() const
{
	return m_lastError;
}

/**
 * Gets a value that indicates whether a socket is connected to a remote host as of the last Send or Receive operation.
 * @return True if the socket was connected to a remote resource as of the most recent operation; otherwise, false.
 */
bool REFSocket::IsConnected() const
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

	return m_bIsConnected;
}

/**
 * Gets a value that indicates whether the Socket is bound to a specific local port.
 * @return True if the socket is bound to a local port; otherwise, false.
 */
bool REFSocket::IsBound() const
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

	return (m_pLocalAddress != NULL);
}

/**
 * Gets the amount of data that has been received from the network and is available to be read.
 * @return The number of bytes of data received from the network and available to be read. Return -1 indicates an error occurred.
 * @see BFX::REFSocket::GetLastError()
 */
int REFSocket::GetAvailable() const
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

	u_long argp = 0;
#ifdef	_WIN32
	int nRetVal = ioctlsocket(m_hSocket, FIONREAD, &argp);
#else
	int nRetVal = ioctl(m_hSocket, FIONREAD, &argp);
#endif
	if (nRetVal == SOCKET_ERROR)
	{
		SocketError error = SocketException::GetLastSocketError();
		const_cast<REFSocket*>(this)->UpdateStatusAfterError(error);
		return -1;
	}

	return (int)argp;
}

/**
 * Gets a value that specifies the size of the receive buffer of the socket.
 * @return The buffer size, in bytes, of the receive buffer. The default is 8192; Specifying -1 indicates an error occurred.
 * @see BFX::REFSocket::GetLastError()
 */
int REFSocket::GetReceiveBufferSize() const
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");
	int nBufferSize = 0;
	bool bRetVal = GetSocketOption(SocketOptionLevel_Socket, SocketOptionName_ReceiveBuffer, nBufferSize);
	return ((!bRetVal) ? -1 : nBufferSize);
}

/**
 * Sets a value that specifies the size of the receive buffer of the socket.
 * @param[in] nBufferSize The buffer size, in bytes, of the receive buffer.
 * @see BFX::REFSocket::GetLastError()
 */
void REFSocket::SetReceiveBufferSize(int nBufferSize)
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");
	BFX_ENSURE1(nBufferSize >= 0, "buffer size out of range.");
	SetSocketOption(SocketOptionLevel_Socket, SocketOptionName_ReceiveBuffer, nBufferSize);
}

/**
 * Gets a value that specifies the amount of time after which a synchronous Receive call will time out.
 * @return The receive timeout value, in milliseconds. The default value is 0, which indicates an infinite timeout period;
 * 		Specifying -1 indicates an error occurred.
 * @see BFX::REFSocket::GetLastError()
 */
int REFSocket::GetReceiveTimeout() const
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");
	int nTimeout = 0;
	bool bRetVal = GetSocketOption(SocketOptionLevel_Socket, SocketOptionName_ReceiveTimeout, nTimeout);
	return ((!bRetVal) ? -1 : (nTimeout == -1 ? 0 : nTimeout));
}

/**
 * Sets a value that specifies the amount of time after which a synchronous Receive call will time out.
 * @param[in] nTimeout The receive timeout value, in milliseconds. Specifying 0 indicates an infinite timeout period.
 * @see BFX::REFSocket::GetLastError()
 */
void REFSocket::SetReceiveTimeout(int nTimeout)
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");
	BFX_ENSURE1(nTimeout >= 0, "timeout out of range.");
	SetSocketOption(SocketOptionLevel_Socket, SocketOptionName_ReceiveTimeout, nTimeout);
}

/**
 * Sets a value that specifies the size of the send buffer of the socket
 * @return The receive timeout value, in milliseconds. The default value is 0, which indicates an infinite timeout period;
 * 		Specifying -1 indicates an error occurred.
 * @see BFX::REFSocket::GetLastError()
 */
int REFSocket::GetSendBufferSize() const
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");
	int nBufferSize = 0;
	bool bRetVal = GetSocketOption(SocketOptionLevel_Socket, SocketOptionName_SendBuffer, nBufferSize);
	return ((!bRetVal) ? -1 : nBufferSize);
}

/**
 * Sets a value that specifies the size of the send buffer of the socket
 * @param[in] nBufferSize The size of the send buffer, in bytes. The default is 8192.
 * @see BFX::REFSocket::GetLastError()
 */
void REFSocket::SetSendBufferSize(int nBufferSize)
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");
	BFX_ENSURE1(nBufferSize >= 0, "Buffer size out of range.");
	SetSocketOption(SocketOptionLevel_Socket, SocketOptionName_ReceiveBuffer, nBufferSize);
}

/**
 * Gets a value that specifies the amount of time after which a synchronous Send call will time out.
 * @return the send timeout value, in milliseconds. The default value is 0 indicates an infinite timeout period;
 * 		Specifying -1 indicates an error occurred.
 * @see BFX::REFSocket::GetLastError()
 */
int REFSocket::GetSendTimeout() const
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");
	int nTimeout = 0;
	bool bRetVal = GetSocketOption(SocketOptionLevel_Socket, SocketOptionName_SendTimeout, nTimeout);
	return ((!bRetVal) ? -1 : (nTimeout == -1 ? 0 : nTimeout));
}

/**
 * Sets a value that specifies the amount of time after which a synchronous Send call will time out.
 * @param[in] nTimeout The send timeout value, in milliseconds. Specifying 0 indicates an infinite timeout period.
 * @see BFX::REFSocket::GetLastError()
 */
void REFSocket::SetSendTimeout(int nTimeout)
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");
	BFX_ENSURE1(nTimeout >= 0, "timeout out of range.");
	SetSocketOption(SocketOptionLevel_Socket, SocketOptionName_SendTimeout, nTimeout);
}

//
// Berkeley socket Implementations.
//

// Creates a accepted socket with given file descriptor and remote address.
REFSocket* REFSocket::CreateAcceptSocket(SOCKET hSocket, REFSocketAddress* pRemoteAddress)
{
	REF<REFSocket> pSocket = new REFSocket(hSocket);

	// set internal state of the socket is inherited from listener.
	UpdateAcceptSocket(pSocket, pRemoteAddress);

	// push the socket into autorelease pool and return it.
	pSocket->Autorelease();
	return pSocket;
}

//
// Internal state of the socket is inherited from listener
//
void REFSocket::UpdateAcceptSocket(REFSocket* pSocket, REFSocketAddress* pRemoteAddress)
{
	BFX_ASSERT(pSocket != NULL);
	BFX_ASSERT(pRemoteAddress != NULL);

	// set internal state of the socket is inherited from listener.
	pSocket->m_addressFamily = m_addressFamily;
	pSocket->m_socketType = m_socketType;
	pSocket->m_protocolType = m_protocolType;
	pSocket->m_pLocalAddress = m_pLocalAddress;
	pSocket->m_pRemoteAddress = pRemoteAddress;

	// the socket to is connected.
	pSocket->m_bIsConnected = true;


	// the new socket will inherit the blocking mode from the accepting socket. if the user desired blocking mode is
	// different from the real blocking mode, we need to force the desired blocking behavior.
	pSocket->m_bIsBlocking = m_bIsBlocking;
	if (m_bIsBlocking != m_bIsRealBlocking)
	{
		bool bCurrent;
		pSocket->TrySetBlocking(m_bIsBlocking, bCurrent);
	}
}

/**
 * Creates a new Socket for a newly created connection.
 * @return A socket for a newly created connection; otherwise NULL, indicates failed to accept new connection.
 * @see BFX::REFSocket::GetLastError()
 */
REFSocket* REFSocket::Accept()
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");
	BFX_ENSURE1(m_pLocalAddress != NULL, "You must call the Bind method before performing this operation.");
	BFX_ENSURE1(m_bIsListening, "You must call the Listen method before performing this operation.");
	// BFX_ENSURE1(m_bIsConnected, "The socket has been disconnected.");

	// Validate blocking mode
	BFX_ENSURE1(m_bIsBlocking || !m_bIsRealBlocking, "Cannot block a call on this socket while an earlier asynchronous call is in progress.");

	// duplicates a memory block from local address.
	int nSockAddrLen;
	const SOCKADDR* pSockAddr = m_pLocalAddress->GetSockAddr(nSockAddrLen);
	SequenceT<BYTE> bufferHolder;
	SOCKADDR* pBuffer = (SOCKADDR*)bufferHolder.GetBuffer(nSockAddrLen);
	memcpy(pBuffer, pSockAddr, nSockAddrLen);

	SOCKLEN_T nBufferLen = nSockAddrLen;
	SOCKET hAcceptedSocket = accept(m_hSocket, pBuffer, &nBufferLen);
	if (hAcceptedSocket == INVALID_SOCKET)
	{
		SocketError error = SocketException::GetLastSocketError();
		UpdateStatusAfterError(error);
		return NULL;
	}

	return CreateAcceptSocket(hAcceptedSocket, m_pLocalAddress->Create(pBuffer, (int)nBufferLen));
}

/**
 * Binds the socket to a local address.
 * @param[in] pLocalAddress An socket address that represents the local network interfaces.
 * @return True if the function is successful; otherwise false, indicates failed to bind socket to specified address.
 * @see BFX::REFSocket::GetLastError()
 */
bool REFSocket::Bind(REFSocketAddress* pLocalAddress)
{
	BFX_REQUIRE(pLocalAddress != NULL && pLocalAddress->IsValid());
	BFX_REQUIRE1(pLocalAddress->GetAddressFamily() == m_addressFamily, "Address family mismatch.");
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

	int nSockAddrLen;
	const SOCKADDR* pSockAddr = pLocalAddress->GetSockAddr(nSockAddrLen);

	int nRetVal = bind(m_hSocket, pSockAddr, nSockAddrLen);
	if (nRetVal == SOCKET_ERROR)
	{
		// failed to bind local address.
		SocketError error = SocketException::GetLastSocketError();
		UpdateStatusAfterError(error);
		return false;
	}

	if (m_pLocalAddress == NULL)
	{
		// save a copy of the SocketAddress so we can use it for Create()
		m_pLocalAddress = pLocalAddress;
	}
	return true;
}

/**
 * Places a Socket in a listening state.
 * @param[in] nBacklog The maximum length of the pending connections queue.
 * @return true if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
 */
bool REFSocket::Listen(int nBacklog)
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");
	BFX_ENSURE(nBacklog >= 0);

	int nRetVal = listen(m_hSocket, nBacklog);
	if (nRetVal == SOCKET_ERROR)
	{
		SocketError error = SocketException::GetLastSocketError();
		UpdateStatusAfterError(error);
		return false;
	}

	m_bIsListening = true;

	return true;
}

/**
 * Establishes a connection to a remote host.
 * @param[in] pRemoteAddress An socket address that represents the remote device.
 * @return True if the function is successful; otherwise false, and a specific error code can be retrieved by calling GetLastError.
 */
bool REFSocket::Connect(REFSocketAddress* pRemoteAddress)
{
	BFX_REQUIRE(pRemoteAddress != NULL && pRemoteAddress->IsValid());
	BFX_REQUIRE1(pRemoteAddress->GetAddressFamily() == m_addressFamily, "Address family mismatch.");
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

	if (m_bIsListening)
	{
		// invalid operation, attempt to uses a listening socket.
		// @See WSAEINVAL
		BFX_ASSERT(false);
		m_lastError = SocketError_InvalidArgument;
		return false;
	}

	// Validate blocking mode
	BFX_ENSURE1(m_bIsBlocking || !m_bIsRealBlocking, "Cannot block a call on this socket while an earlier asynchronous call is in progress.");

	int nSockAddrLen;
	const SOCKADDR* pSockAddr = pRemoteAddress->GetSockAddr(nSockAddrLen);
	if (connect(m_hSocket, pSockAddr, nSockAddrLen) == SOCKET_ERROR)
	{
		// failed to connect to remote address.
		SocketError error = SocketException::GetLastSocketError();
		UpdateStatusAfterError(error);
		return false;
	}

	if (m_pRemoteAddress == NULL)
	{
		// save a copy of the SocketAddress so we can use it for Create()
		m_pRemoteAddress = pRemoteAddress;
	}

	m_bIsConnected = true;

	return true;
}

/**
 * Establishes a connection to a remote host.
 * @param[in] strRemoteAddress The IP address of the remote host.
 * @param[in] nPort The port number of the remote host.
 * @return True if the function is successful; otherwise false, and a specific error code can be retrieved by calling GetLastError.
 */
bool REFSocket::Connect(const String& strRemoteAddress, int nPort)
{
	REFSocketAddress* pRemoteAddress = new REFInternetAddress(strRemoteAddress, nPort);
	BFX_ENSURE(pRemoteAddress->IsValid());
	return Connect(pRemoteAddress);
}

/**
 * Determines the status of the socket.
 * @param[in] nMicroseconds The time to wait for a response, in microseconds. Specifying -1 indicates an infinite timeout period.
 * @param[in] mode One of the BFX::SocketPollMode values.
 * @return The status of the socket based on the polling mode value passed in the mode parameter.\n
 *	SocketPollMode_Read:
 *		true if Listen has been called and a connection is pending;
 *		true if data is available for reading;
 *		true if the connection has been closed, reset, or terminated;
 *		otherwise, returns false.\n
 *	SocketPollMode_Write:
 *		true if processing a Connect, and the connection has succeeded;
 *		true if data can be sent;
 *		otherwise, returns false.\n
 *	SelectPoolMode_Error:
 *		true if processing a Connect that does not block, and the connection has failed;
 *		true if OutOfBandInline is not set and out-of-band data is available;
 *		otherwise, returns false.\n
 *	Error occurs:
 *		false, and a specific error code can be retrieved by calling GetLastError.
 */
bool REFSocket::Poll(int nMicroseconds, SocketPollMode mode)
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

	const int MICROCNV = 1000000;
	timeval timeout;
	fd_set fds;

	FD_ZERO(&fds);
	FD_SET(m_hSocket, &fds);

	// NOTE: nMicroseconds == -1 indicates a blocking operation.
	if (nMicroseconds != -1)
	{
		timeout.tv_sec = nMicroseconds / MICROCNV;
		timeout.tv_usec = nMicroseconds % MICROCNV;
	}

	int nSocketCount = select(m_hSocket + 1, (mode == SocketPollMode_Read) ? &fds : NULL,
			(mode == SocketPollMode_Write) ? &fds : NULL, (mode == SocketPollMode_Error) ? &fds : NULL,
			(nMicroseconds != -1) ? &timeout : NULL);
	if (nSocketCount == SOCKET_ERROR)
	{
		// error occurred, set internal flags and return false.
		SocketError error = SocketException::GetLastSocketError();
		UpdateStatusAfterError(error);
		return false;
	}

	return (FD_ISSET(m_hSocket, &fds) != 0);
}

/**
 * Receives data from a bound socket into a receive buffer.
 * @param[out] pBuffer A pointer to a buffer that is the storage location for the received data.
 * @param[in] iOffset The position in the buffer parameter to store the received data.
 * @param[in] nSize The number of bytes to receive.
 * @param[in] socketFlags A bitwise combination of the BFX::SocketFlags values.
 * @return The number of bytes received.
 */
int REFSocket::Receive( BYTE* pBuffer, int iOffset, int nSize, SocketFlags socketFlags)
{
	BFX_REQUIRE1(pBuffer != NULL || nSize == 0, "Null buffer pointer with nonzero buffer size.");
	BFX_REQUIRE1(iOffset >= 0 , "Non-negative buffer offset required.");
	BFX_REQUIRE1(nSize >= 0 , "Non-negative buffer size required.");
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

	// Validate blocking mode
	BFX_ENSURE1(m_bIsBlocking || !m_bIsRealBlocking, "Cannot block a call on this socket while an earlier asynchronous call is in progress.");

	int nBytesTransferred = recv(m_hSocket, (char*)pBuffer + iOffset, nSize, socketFlags);
	if (nBytesTransferred == SOCKET_ERROR)
	{
		// failed to receive data.
		SocketError error = SocketException::GetLastSocketError();
		UpdateStatusAfterError(error);
	}

	return nBytesTransferred;
}

/**
 * Receives the specified number of bytes of data from the specified remote address.
 * @param[out] pBuffer A pointer to a buffer that is the storage location for the received data.
 * @param[in] iOffset The position in the buffer parameter to store the received data.
 * @param[in] nSize The number of bytes to receive.
 * @param[in] pRemoteAddress An socket address that represents the remote server.
 * @return The number of bytes received.
 */
int REFSocket::ReceiveFrom( BYTE* pBuffer, int iOffset, int nSize, REFSocketAddress* pRemoteAddress)
{
	// Validate blocking mode
	BFX_ENSURE1(m_bIsBlocking || !m_bIsRealBlocking, "Cannot block a call on this socket while an earlier asynchronous call is in progress.");
	BFX_ASSERT(false);
	return -1;
}

/**
 * Sends the specified number of bytes of data to a connected socket.
 * @param[in] pBuffer A pointer to a buffer that contains the data to be sent.
 * @param[in] iOffset The position in the data buffer at which to begin sending data.
 * @param[in] nSize The number of bytes to send.
 * @param[in] socketFlags A bitwise combination of the BFX::SocketFlags values.
 * @return The number of bytes send to the socket.
 */
int REFSocket::Send(const BYTE* pBuffer, int iOffset, int nSize, SocketFlags socketFlags)
{
	BFX_REQUIRE1(pBuffer != NULL || nSize == 0, "Null buffer pointer with nonzero buffer size.");
	BFX_REQUIRE1(iOffset >= 0 , "Non-negative buffer offset required.");
	BFX_REQUIRE1(nSize >= 0 , "Non-negative buffer size required.");
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

	// Validate blocking mode
	BFX_ENSURE1(m_bIsBlocking || !m_bIsRealBlocking, "Cannot block a call on this socket while an earlier asynchronous call is in progress.");

	int nBytesTransferred = send(m_hSocket, (char*)pBuffer + iOffset, nSize, socketFlags);
	if (nBytesTransferred == SOCKET_ERROR)
	{
		// failed to send data, update internal state.
		SocketError error = SocketException::GetLastSocketError();
		UpdateStatusAfterError(error);
	}

	return nBytesTransferred;
}

/**
 * Sends the specified number of bytes of data to the specified remote address.
 * @param[in] pBuffer A pointer to a buffer that contains the data to be sent.
 * @param[in] iOffset The position in the data buffer at which to begin sending data.
 * @param[in] nSize The number of bytes to send.
 * @param[in] pRemoteAddress The remote address to be send.
 */
int REFSocket::SendTo(const BYTE* pBuffer, int iOffset, int nSize, REFSocketAddress* pRemoteAddress)
{
	// Validate blocking mode
	BFX_ENSURE1(m_bIsBlocking || !m_bIsRealBlocking, "Cannot block a call on this socket while an earlier asynchronous call is in progress.");

	BFX_ASSERT(false);
	return -1;
}

/**
 * Gets the value that indicates whether the socket is blocking mode.
 * @return True if the socket is blocking; otherwise false.
 */
bool REFSocket::IsBlocking() const
{
	return m_bIsBlocking;
}

/**
 * Sets the value that indicates whether the socket is blocking mode.
 * @param[in] bBlocking True if the socket will block; otherwise false. the default value is true.
 */
void REFSocket::SetBlocking(bool bBlocking)
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

	bool bCurrent;

	SocketError error = TrySetBlocking(bBlocking, bCurrent);
	if (error != SocketError_Success)
	{
		UpdateStatusAfterError(error);
		return;
	}
	// update to desired state.
	m_bIsBlocking = bCurrent;
}

// Attempt to set blocking mode.
// This version will ignore failures, and will update internal block state on success.
SocketError REFSocket::TrySetBlocking(bool bDesired,  bool& bCurrent)
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

	u_long nBlocking = bDesired ? 0 : -1;
	SocketError error = SocketError_Success;

#ifdef	_WIN32
	int nRetVal = ioctlsocket(m_hSocket, FIONBIO, &nBlocking);
#else
	int nRetVal = ioctl(m_hSocket, FIONBIO, &nBlocking);
#endif
	if (nRetVal == SOCKET_ERROR)
	{
		error = SocketException::GetLastSocketError();
	}
	else
	{
		// we will update real blocking state but only on successful call.
		m_bIsRealBlocking = (nBlocking == 0);
	}

	bCurrent = m_bIsRealBlocking;
	return error;
}


/**
 * Gets the specified Socket option settings.
 * @param[in] optionLevel
 * @param[in] optionName
 * @param[out] bOptionValue A boolean reference value to retrieve the option settings.
 * @return True if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
 */
bool REFSocket::GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName,  bool& bOptionValue) const
{
	int nOptionValue = 0;
	bool bRetVal = GetSocketOption(optionLevel, optionName, nOptionValue);
	if (bRetVal)
	{
		bOptionValue = (nOptionValue == 0) ? false : true;
		return true;
	}
	return false;
}

/**
 * Gets the specified Socket option settings.
 * @param[in] optionLevel
 * @param[in] optionName
 * @param[out] nOptionValue A integer reference value to retrieve the option settings.
 * @return True if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
 */
bool REFSocket::GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName,  INT32& nOptionValue) const
{
	int nOptionValueSize = sizeof(INT32);
	return GetSocketOption(optionLevel, optionName, &nOptionValue, &nOptionValueSize);
}

/**
 * Gets the specified Socket option settings.
 * @param[in] optionLevel
 * @param[in] optionName
 * @param[out] pOptionValue A pointer to the buffer to retrieve the option settings.
 * @param[in,out] pOptionValueSize A pointer to the size of the pOptionValue buffer, in bytes.
 * @return True if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
 */
bool REFSocket::GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName,  void* pOptionValue, INOUT int* pOptionValueSize) const
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

	SOCKLEN_T nOutValueLen = *pOptionValueSize;
	int nRetVal = getsockopt(m_hSocket, optionLevel, optionName, (char*)pOptionValue, &nOutValueLen);
	if (nRetVal == SOCKET_ERROR)
	{
		// set our last error state, and update our internal state after this socket error.
		SocketError error = SocketException::GetLastSocketError();

		// NOTE: this is a bad idea to modify member variables from a const method.
		const_cast<REFSocket*>(this)->UpdateStatusAfterError(error);
		return false;
	}
	*pOptionValueSize = (int)nOutValueLen;
	return true;
}

/**
 * Sets the specified Socket option to the specified value.
 * @param[in] optionLevel One of the BFX::SocketOptionLevel values.
 * @param[in] optionName One of the BFX::SocketOptionName values.
 * @param[in] bOptionValue The boolean value of the option.
 * @return True if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
 */
bool REFSocket::SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, bool bOptionValue)
{
	return SetSocketOption(optionLevel, optionName, bOptionValue ? 0 : 1);
}

/**
 * Sets the specified Socket option to the specified value.
 * @param[in] optionLevel One of the BFX::SocketOptionLevel values.
 * @param[in] optionName One of the BFX::SocketOptionName values.
 * @param[in] nOptionValue The integer value of the option.
 * @return True if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
 */
bool REFSocket::SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, INT32 nOptionValue)
{
	return SetSocketOption(optionLevel, optionName, &nOptionValue, sizeof(INT32));
}

/**
 * Sets the specified Socket option to the specified value.
 * @param[in] optionLevel One of the BFX::SocketOptionLevel values.
 * @param[in] optionName One of the BFX::SocketOptionName values.
 * @param[in] pOptionValue A pointer to the buffer in which the value for the requested option is supplied.
 * @param[in] nOptionValueSize The size of the pOptionValue buffer in bytes.
 * @return True if successful; otherwise, false, and a specific error code can be retrieved by calling GetLastError.
 */
bool REFSocket::SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, const void* pOptionValue, int nOptionValueSize)
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");
	BFX_REQUIRE1(pOptionValue != NULL || nOptionValueSize == 0, "Null value with not zero length is disallowed.");

	int nRetVal = setsockopt(m_hSocket, optionLevel, optionName, (const char*)pOptionValue, nOptionValueSize);
	if (nRetVal == SOCKET_ERROR)
	{
		SocketError error = SocketException::GetLastSocketError();
		UpdateStatusAfterError(error);
		return false;
	}
	return true;
}

/**
 * Sets low-level operating modes for the socket using numerical control codes.
 * @param[in] ioControlCode A command to perform on the socket.
 * @param[in,out] pOptionValue A pointer to a parameter for command.
 * @return True if successful, otherwise, false, and a specific error code can be retrieved by calling GetLastError.
 */
bool REFSocket::IOControl(SocketIOControlCode ioControlCode, INOUT void* pOptionValue)
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

#ifdef	_WIN32
	int nRetVal = ioctlsocket(m_hSocket, ioControlCode, (u_long*)pOptionValue);
#else
	int nRetVal = ioctl(m_hSocket, ioControlCode, pOptionValue);
#endif
	if (nRetVal != 0)
	{
		SocketError error = SocketException::GetLastSocketError();
		UpdateStatusAfterError(error);
		return false;
	}
	return true;
}

/**
 * Disables sends and/or receives on a socket.
 * @param[in] how One of the BFX::SocketShutdown values that specifies the operation that will no longer be allowed.
 */
void REFSocket::Shutdown(SocketShutdown how)
{
	BFX_ENSURE1(IsValid(), "Attempt to uses a closed socket.");

	int nRetVal = shutdown(m_hSocket, how);
	if (nRetVal != 0)
	{
		SocketError error = SocketException::GetLastSocketError();
		// skip the case of socket already closed.
		if (error != SocketError_NotSocket)
		{
			// update our internal state.
			UpdateStatusAfterError(error);
			return;
		}
	}
	m_bIsConnected = false;

	bool bCurrent;
	TrySetBlocking(m_bIsRealBlocking, bCurrent);
}

/**
 * Closes the Socket connection and releases all associated resources with a specified timeout to allow queued data to be sent.
 * @param[in] nTimeout Wait up to timeout seconds to send any remaining data, then close the socket. The default value is -1, witch
 *	indicates close with existing user-specified linger option. Specifying 0 indicates close abortively.
 */
void REFSocket::Close(int nTimeout)
{
	m_bIsConnected = false;

	if (nTimeout == 0)
	{
		// abort.
		goto CloseNow;
	}
	else
	{
		int nRetVal;
		if (!m_bIsBlocking || !m_bIsRealBlocking)
		{
			u_long nBlockCmd = 0;
#ifdef	_WIN32
			nRetVal = ioctlsocket(m_hSocket, FIONBIO, &nBlockCmd);
#else
			nRetVal = ioctl(m_hSocket, FIONBIO, &nBlockCmd);
#endif
			BFX_ASSERT(nRetVal == 0);
		}

		if (nTimeout <= 0)
		{
			// close with existing user-specified linger option.
			// ??? m_hSocket.CloseAsIs();
			goto CloseNow;
		}

		// since our timeout is in ms and linger is in seconds, implement our own sortof linger here.
		nRetVal = shutdown(m_hSocket, SocketShutdown_Send);
		// BFX_ASSERT(nRetVal == 0);

		// this should give us a timeout in milliseconds.
		nRetVal = setsockopt(m_hSocket, SocketOptionLevel_Socket, SocketOptionName_ReceiveTimeout, (const char*)&nTimeout,
				sizeof(int));
		if (nRetVal != 0)
		{
			// abort.
			goto CloseNow;
		}

		nRetVal = recv(m_hSocket, NULL, 0, SocketFlags_None);
		if (nRetVal != 0)
		{
			// We got a timeout - abort.
			goto CloseNow;
		}

		// We got a FIN or data. Use ioctlsocket to find out which.
		u_long nDataAvailable = 0;
#ifdef	_WIN32
		nRetVal = ioctlsocket(m_hSocket, FIONREAD, &nDataAvailable);
#else
		nRetVal = ioctl(m_hSocket, FIONREAD, &nDataAvailable);
#endif
		if (nRetVal != 0 || nDataAvailable != 0)
		{
			// If we have data or don't know, safest thing is to reset.
			goto CloseNow;
		}

		// We got a FIN.  It'd be nice to block for the remainder of the timeout for the handshake to finsh.
		// Since there's no real way to do that, close the socket with the user's preferences.  This lets
		// the user decide how best to handle this case via the linger options.
		// ??? m_hSocket.CloseAsIs();
		goto CloseNow;
	}

CloseNow:
#ifdef	_WIN32
	closesocket(m_hSocket);
#else	//	_WIN32
	close(m_hSocket);
#endif	//	!_WIN32
	m_hSocket = INVALID_SOCKET;
}

// Updates the status of a connected socket on which a failure occurred.
// It'll check if the connection is still open and if need to update the internal state.
void REFSocket::UpdateStatusAfterError(SocketError error)
{
	// if we already know the socket is disconnected, we don't need to do anything else.
	if (m_bIsConnected
			&& (m_hSocket == INVALID_SOCKET
					|| (error != SocketError_WouldBlock && error != SocketError_IOPending
							&& error != SocketError_NoBufferSpaceAvailable)))
	{
		// the socket is no longer a valid socket
		m_bIsConnected = false;
	}
	m_lastError = error;
}

} /* namespace BFX */
